﻿#define _CRT_SECURE_NO_WARNINGS 1

#include "ThreadCache.hpp"
#include "CentralCache.hpp"

void* ThreadCache::Allocate(size_t size) {
	assert(size <= MAX_BYTES);

	// 获取对齐后的内存大小
	size_t alignSize = SizeClass::RoundUp(size);
	// 获取对应的自由链表索引
	size_t index = SizeClass::Index(size);
	// 如果此时该索引的自由链表不为空，则直接从自由链表中获取对象
	if (!_freeLists[index].Empty()) {
		// 有可用对象，直接从自由链表中获取
		return _freeLists[index].Pop();
	}
	else {
		// 从中心缓存获取对象
		return FetchFromCentralCache(index, alignSize);
	}
}

// 释放内存对象
void ThreadCache::Deallocate(void* ptr, size_t size) {
	assert(ptr);
	assert(size <= MAX_BYTES);

	// 找出对应映射的链表桶，并将对象放入链表中
	size_t index = SizeClass::Index(size);
	_freeLists[index].Push(ptr);

	// 当链表过长时，回收内存到中心缓存
	if (_freeLists[index].Size() >= _freeLists[index].MaxSize()) {
		ListTooLong(_freeLists[index], size);
	}
}

// 从中心缓存获取对象
void* ThreadCache::FetchFromCentralCache(size_t index, size_t size) {
	 // 慢开始反馈调节算法
	 // 1. 最开始不会一次向 central cache 一次批量要太多，因为要太多了可能用不完
	 // 2. 如果你不要这个 size 大小内存需求，那么 batchNum 就会不断增长，知道上线
	 // 3. size 也越多，一次向 central cache 要的内存就越少 512
	 // 4. size 也越少，一次向 central cache 要的内存就越多 2
	size_t batchNum = min(_freeLists[index].MaxSize(), SizeClass::NumMoveSize(size));


	if (_freeLists[index].MaxSize() == batchNum) {
		_freeLists[index].MaxSize() += 1;
	}

	void* start = nullptr;
	void* end = nullptr;
	size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);
	assert(actualNum >= 1);

	// 申请到对象的个数是一个，则直接将这一个对象返回即可
	if (actualNum == 1) {
		assert(start == end);
		return start;
	}
	// 申请到对象的个数是多个，还需要将剩下的对象挂到thread cache中对应的哈希桶
	else {
		_freeLists[index].PushRange(NextObj(start), end, actualNum - 1);
		return start;
	}


	return nullptr;
}


// 释放对象时，链表过⻓时，回收内存回到中⼼缓存
void ThreadCache::ListTooLong(FreeList& list, size_t size) {
	void* start = nullptr;
	void* end = nullptr;
	// 弹出一段内存对象
	list.PopRange(start, end, list.MaxSize());

	CentralCache::GetInstance()->ReleaseListToSpans(start, size);
}